#ifndef MONITOR_GRAPHIC_H_
#define MONITOR_GRAPHIC_H_
#include <gtkmm/drawingarea.h>
#include <fstream>
#include <iostream>
#include <vector>
#include <string>
#include <cairomm/context.h>
#include <glibmm/main.h>
#include <pangomm.h>

using namespace std;

class Point {
public:
	float x;
	float y;
	Point(float x, float y) {
		this->x = x;
		this->y = y;
	}
	Point(const Point& other) {
		this->x = other.x;
		this->y = other.y;
	}
};

class Graphic: public Gtk::DrawingArea {
private:
	string path;
	//Tamanio de la ventana del grafico
	int draw_window_width;
	int draw_window_height;

	//Posiciones iniciales y finales de los ejes cartesianos
	float x_origin;
	float y_origin;
	float x_end;
	float y_end;

	//Maximos valores de x e y
	float x_max_value;
	float y_max_value;

	//Tamanio de las divisiones (en pixels)
	float x_division_size;
	float y_division_size;

public:
	Graphic(int window_width, int window_height, string* path) {
		this->path = *path;
		this->draw_window_width = window_width;
		this->draw_window_height = window_height;
		this->x_origin = 20;
		this->y_origin = 20;
		this->x_end = draw_window_width - 20;
		this->y_end = draw_window_height - 20;
		set_size_request(draw_window_width, draw_window_height);
		Glib::signal_timeout().connect(
				sigc::mem_fun(*this, &Graphic::on_timeout), 5000);
#ifndef GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED
		//Conecta el sigan handler si no esta redefinido
		signal_draw().connect(sigc::mem_fun(*this, &Graphic::on_draw), false);
#endif //GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED
	}
	virtual ~Graphic() {

	}
	float TransformX(float x) {
		return x;
	}

	float TransformY(float y) {
		return (draw_window_height - y);
	}

	void DrawLine(float xs, float xe, float ys, float ye,
			const Cairo::RefPtr<Cairo::Context>& cr) {
		cr->move_to(TransformX(x_origin + xs * x_division_size),
				TransformY(y_origin + ys * y_division_size));
		cr->line_to(TransformX(x_origin + xe * x_division_size),
				TransformY(y_origin + ye * y_division_size));
		cr->stroke();

	}
	//Lee los puntos del archivo
	vector<Point> ReadPoints() {
		vector<Point> points;
		float x;
		float y;
		ifstream ifs;
		ifs.open(path.c_str(), ios::in);
		if (ifs.is_open()) {
			while (ifs.good()) {
				if (ifs.good()) {
					ifs >> x;
					ifs >> y;
					Point p(x, y);
					points.push_back(p);
				}
			}
			ifs.close();
		} else {
			Point p(0, 0);
			points.push_back(p);
		}

		return points;
	}
protected:
	//Redibuja al llegar al timeout definido
	bool on_timeout() {
		Glib::RefPtr < Gdk::Window > win = get_window();
		if (win) {
			Gdk::Rectangle r(0, 0, this->draw_window_width,
					this->draw_window_height);
			win->invalidate_rect(r, false);
		}
		return true;
	}

	//Dibuja el texto de la descripcion de los ejes
	void DrawAxisDescription(const Cairo::RefPtr<Cairo::Context>& cr) {
		//Defino la fuente
		Pango::FontDescription font;
		font.set_family("Monospace");
		font.set_weight(Pango::WEIGHT_BOLD);
		font.set_size(PANGO_SCALE * 8);

		//El texto del eje y
		Glib::RefPtr < Pango::Layout > y_layout = create_pango_layout(
				"Stored(Kbs)");
		y_layout->set_font_description(font);

		//Lo posicion al final del eje vertical
		cr->move_to(TransformX(0), TransformY(draw_window_height - 5));
		y_layout->show_in_cairo_context(cr);

		//El texto del eje x
		Glib::RefPtr < Pango::Layout > x_layout = create_pango_layout(
				"Time(seconds)");
		x_layout->set_font_description(font);

		//Lo posiciono al final del eje x
		cr->move_to(TransformX(draw_window_width - 100), TransformY(17));
		x_layout->show_in_cairo_context(cr);
	}

	//Lo que ejecuta al detectar la senial de dibujar
	virtual bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr) {

		cr->set_line_width(1.0);
		//Color negro
		cr->set_source_rgb(0, 0, 0);
		//Dibujando eje vertical
		cr->move_to(TransformX(x_origin), TransformY(y_origin));
		cr->line_to(TransformX(x_origin), TransformY(y_end));

		//Dibujando eje horizontal
		cr->move_to(TransformX(x_origin), TransformY(y_origin));
		cr->line_to(TransformX(x_end), TransformY(y_origin));
		DrawAxisDescription(cr);
		cr->stroke();
		vector<Point> points = ReadPoints();

		this->x_max_value = points.back().x * 1.2;
		this->y_max_value = points.back().y * 1.2;
		this->x_division_size = (x_end - x_origin) / x_max_value;
		this->y_division_size = (y_end - y_origin) / y_max_value;

		float old_x = 0;
		float old_y = 0;
		//Dibujo todos los puntos
		for (vector<Point>::iterator it = points.begin(); it != points.end();
				++it) {
			float new_x = it->x;
			float new_y = it->y;
			cr->set_source_rgb(1, 0, 0);
			DrawLine(old_x, new_x, old_y, new_y, cr);
			/*cout << "Graficando punto: (" << old_x << "," << old_y << ") -> ("
			 << new_x << "," << new_y << ")" << endl;*/
			old_x = new_x;
			old_y = new_y;
		}

		return true;
	}
};

#endif /* MONITOR_FIRST_H_ */
